#include "ICamera.h"

ICamera::ICamera()
{
	D3DXMatrixIdentity(&m_View);
	D3DXMatrixIdentity(&m_Proj);
	D3DXMatrixIdentity(&m_ViewProj);
	D3DXMatrixIdentity(&m_WorldMatrix);

	m_PosW   = Vec3(0.0f, 0.0f, 0.0f);
	m_RightW = Vec3(1.0f, 0.0f, 0.0f);
	m_UpW    = Vec3(0.0f, 1.0f, 0.0f);
	m_LookW  = Vec3(0.0f, 0.0f, 1.0f);

	// adjust to a value that makes sense for application
	m_Speed  = 10.0f;

	//Set a constant rotation on that axis
	//m_PosX = 0;
	//m_PosY = 0;

	m_Pitch = 0.0f;

	UpdateFrustum = true;
	FrustumDraw = true;

	m_Fov = D3DX_PI * 0.25f; 
	//m_Aspect = (float)ViewPort.Width/ViewPort.Height; 
	m_Near = 10.0f; 
	m_Far = 1000000.0f;
}

const Matrix4x4& ICamera::GetViewMatrix() const
{
	return m_View;
}

const Matrix4x4& ICamera::GetProjectionMatrix() const
{
	return m_Proj;
}

const Matrix4x4& ICamera::GetViewProjMatrix()
{
	// Rebuild as the matrix returned was sometimes wrong
	//BuildView();
	m_ViewProj = m_View * m_Proj;

	return m_ViewProj;
}

const Matrix4x4& ICamera::GetWorldMatrix() const
{
	return m_WorldMatrix;
}

const Vec3& ICamera::GetRight() const
{
	return m_RightW;
}

const Vec3& ICamera::GetUp() const
{
	return m_UpW;
}

const Vec3& ICamera::GetLook() const
{
	return m_LookW;
}

Vec3& ICamera::GetCameraPos()
{
	return m_PosW;
}

void ICamera::LookAt(Vec3& pos, Vec3& target, Vec3& up)
{
	//D3DXMatrixLookAtLH(mView, pos, target, up);

	//get the forward vector from the target to the position
	Vec3 L = target - pos;
	D3DXVec3Normalize(&L, &L);

	//get the right vector from the cross of the up vector and forward vector
	Vec3 R;
	D3DXVec3Cross(&R, &up, &L);
	D3DXVec3Normalize(&R, &R);

	//get the up vector from the cross of the right hand and forward vector.
	Vec3 U;
	D3DXVec3Cross(&U, &L, &R);
	D3DXVec3Normalize(&U, &U);

	m_PosW   = pos;
	m_RightW = R;
	m_UpW    = U;
	m_LookW  = L;

	BuildView();

	m_ViewProj = m_View * m_Proj;
}

Matrix4x4 ICamera::CalcViewMatrix(Vec3& pos, Vec3& target, Vec3& up)
{
	Vec3 L = target - pos;
	D3DXVec3Normalize(&L, &L);

	Vec3 R;
	D3DXVec3Cross(&R, &up, &L);
	D3DXVec3Normalize(&R, &R);

	Vec3 U;
	D3DXVec3Cross(&U, &L, &R);
	D3DXVec3Normalize(&U, &U);

	Vec3 PosW   = pos;
	Vec3 RightW = R;
	Vec3 UpW    = U;
	Vec3 LookW  = L;

	Matrix4x4 View;

	//Update
	// Rotate ICamera's look and up vectors around the ICamera's right vector.
	Matrix4x4 Ri;
	D3DXMatrixRotationAxis(&Ri, &RightW, m_Pitch);
	D3DXVec3TransformCoord(&LookW, &LookW, &Ri);
	D3DXVec3TransformCoord(&UpW, &UpW, &Ri);

	// Rotate ICamera axes about the world's y-axis.
	D3DXMatrixRotationY(&Ri, m_AngleY);
	D3DXVec3TransformCoord(&RightW, &RightW, &Ri);
	D3DXVec3TransformCoord(&UpW, &UpW, &Ri);
	D3DXVec3TransformCoord(&LookW, &LookW, &Ri);

	//BuildView

	// Keep ICamera's axes orthogonal to each other and of unit length.
	D3DXVec3Normalize(&LookW, &LookW);

	D3DXVec3Cross(&UpW, &LookW, &RightW);
	D3DXVec3Normalize(&UpW, &UpW);

	D3DXVec3Cross(&RightW, &UpW, &LookW);
	D3DXVec3Normalize(&RightW, &RightW);

	// Fill in the view matrix entries.
	float x = -D3DXVec3Dot(&PosW, &RightW);
	float y = -D3DXVec3Dot(&PosW, &UpW);
	float z = -D3DXVec3Dot(&PosW, &LookW);

	View(0,0) = RightW.x; 
	View(1,0) = RightW.y; 
	View(2,0) = RightW.z; 
	View(3,0) = x;   

	View(0,1) = UpW.x;
	View(1,1) = UpW.y;
	View(2,1) = UpW.z;
	View(3,1) = y;  

	View(0,2) = LookW.x; 
	View(1,2) = LookW.y; 
	View(2,2) = LookW.z; 
	View(3,2) = z;   

	View(0,3) = 0.0f;
	View(1,3) = 0.0f;
	View(2,3) = 0.0f;
	View(3,3) = 1.0f;

	return View;
}

void ICamera::BuildProjectionMatrix(float fov, float aspect, float nearZ, float farZ)
{

	//D3DVIEWPORT9 ViewPort;
	//TheD3DObj::Instance()->GetD3DDevice()->GetViewport(&ViewPort);
	//D3DXMatrixPerspectiveFovLH(&m_ProjectionMatrix,D3DX_PI * 0.5f, 
	//(float)ViewPort.Width/ViewPort.Height, 1.0f, 5000.0f);

	//The aspect ratio depends on the backbuffer dimensions, which can 
	//possibly change after a reset.  So rebuild the projection matrix.
	//float w = (float)md3dPP.BackBufferWidth;
	//float h = (float)md3dPP.BackBufferHeight;

	//m_Frustrum.Init(fov, aspect, nearZ, farZ, m_RightW, m_UpW, m_LookW);

	//D3DXMatrixPerspectiveFovLH(&m_Proj, m_Frustrum.m_Fov, m_Frustrum.m_Aspect, m_Frustrum.m_Near, m_Frustrum.m_Far);

	D3DXMatrixPerspectiveFovLH(&m_Proj, fov, aspect, nearZ, farZ);
	
	//m_Frustum.Init(m_View,m_Proj);

	m_Fov = fov; 
	m_Aspect = aspect; 
	m_Near = nearZ; 
	m_Far = farZ;
}

void ICamera::SetSpeed(float s)
{
	m_Speed = s;
}

bool ICamera::isVisible(Vec3 pos)
{
	return true;//m_Frustum.Inside(pos);
}

bool ICamera::isVisible(Vec3 pos, const float radius)
{
	return true;//m_Frustum.Inside(pos, radius);
}

void ICamera::BuildView()
{
	// Keep ICamera's axes orthogonal to each other and of unit length.
	D3DXVec3Normalize(&m_LookW, &m_LookW);

	D3DXVec3Cross(&m_UpW, &m_LookW, &m_RightW);
	D3DXVec3Normalize(&m_UpW, &m_UpW);

	D3DXVec3Cross(&m_RightW, &m_UpW, &m_LookW);
	D3DXVec3Normalize(&m_RightW, &m_RightW);

	// Fill in the view matrix entries.
	float x = -D3DXVec3Dot(&m_PosW, &m_RightW);
	float y = -D3DXVec3Dot(&m_PosW, &m_UpW);
	float z = -D3DXVec3Dot(&m_PosW, &m_LookW);

	m_View(0,0) = m_RightW.x; 
	m_View(1,0) = m_RightW.y; 
	m_View(2,0) = m_RightW.z; 
	m_View(3,0) = x;   

	m_View(0,1) = m_UpW.x;
	m_View(1,1) = m_UpW.y;
	m_View(2,1) = m_UpW.z;
	m_View(3,1) = y;  

	m_View(0,2) = m_LookW.x;  
	m_View(1,2) = m_LookW.y; 
	m_View(2,2) = m_LookW.z; 
	m_View(3,2) = z;   

	m_View(0,3) = 0.0f;
	m_View(1,3) = 0.0f;
	m_View(2,3) = 0.0f;
	m_View(3,3) = 1.0f;

	m_ViewProj = m_View * m_Proj;
}

void ICamera::SetCameraPos(const Vec3& Pos)
{
	m_PosW = Pos;
}

void ICamera::SetCameraTarget(const Vec3& Target)
{
	Vec3 L = Target - m_PosW;
	D3DXVec3Normalize(&L, &L);
	m_LookW  = L;
}

void ICamera::BuildReflectionViewMatrix()
{
	D3DXPLANE reflectionPlane;
	Vec3 point(0.0f, 0.0f, 0.0f);

	// negative of the normal vector
	Vec3 normal(0.0f, 1.0f, 0.0f);

	// create and normalise the plane
	D3DXPlaneFromPointNormal(&reflectionPlane,&point,&normal);
	//D3DXPlaneNormalize(&clipPlane,&clipPlane);

	//was making sure the currect view matrix was correct, may not need
	BuildView();

	Matrix4x4 Reflect;
	D3DXMatrixReflect(&Reflect, &reflectionPlane);

	// First get current view matrix
	Matrix4x4 OldView = m_View;

	// Then create a reflection view matrix
	D3DXMatrixMultiply(&m_ReflectionViewMatrix, &Reflect, &OldView);
}

Matrix4x4 ICamera::GetReflectionViewMatrix()
{
	BuildReflectionViewMatrix();
	return m_ReflectionViewMatrix;
}

void ICamera::SetViewMatrix(const Matrix4x4& View)
{
	m_View = View;
}
